#include "Input.h"

//-----------------------------------------------------------------------------
// Defines, constants, and global variables
//-----------------------------------------------------------------------------
#define SAFE_DELETE(p)  { if(p) { delete (p);     (p)=NULL; } }
#define SAFE_RELEASE(p) { if(p) { (p)->Release(); (p)=NULL; } }


// Each device object for which you want to receive input must have an entry
// in this DIOBJECTDATAFORMAT array which is stored in the custom DIDATAFORMAT.
// The DIOBJECTDATAFORMAT maps detected device object to a particular offset
// within MouseState structure declared above. Inside the input routine, a
// MouseState structure is provided to the GetDeviceState method, and
// DirectInput uses this offset to store the input data in the provided
// structure. 
// 
// Any of the elements which are not flagged as DIDFT_OPTIONAL, and
// which describe a device object which is not found on the actual device will
// cause the SetDeviceFormat call to fail. For the format defined below, the
// system mouse must have an x-axis, y-axis, and at least one button. 

DIOBJECTDATAFORMAT g_aObjectFormats[] =
{
    { &GUID_XAxis, FIELD_OFFSET( MouseState, lAxisX ),    // X axis
        DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
	{ &GUID_YAxis, FIELD_OFFSET( MouseState, lAxisY ),    // Y axis
        DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
	{ &GUID_ZAxis, FIELD_OFFSET( MouseState, lAxisZ ),    // Z axis - Mouse Wheel
        DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
    { 0, FIELD_OFFSET( MouseState, abButtons[0] ),        // Button 0
        DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
    { 0, FIELD_OFFSET( MouseState, abButtons[1] ),        // Button 1 (optional)
        DIDFT_BUTTON | DIDFT_ANYINSTANCE | DIDFT_OPTIONAL, 0 },
    { 0, FIELD_OFFSET( MouseState, abButtons[2] ),        // Button 2 (optional)
        DIDFT_BUTTON | DIDFT_ANYINSTANCE | DIDFT_OPTIONAL, 0 }
};
#define numMouseObjects (sizeof(g_aObjectFormats) / sizeof(DIOBJECTDATAFORMAT))

// Finally, the DIDATAFORMAT is filled with the information defined above for 
// our custom data format. The format also defines whether the returned axis 
// data is absolute or relative. Usually mouse movement is reported in relative 
// coordinates, but our custom format will use absolute coordinates. 

DIDATAFORMAT            g_dfMouse =
{
    sizeof( DIDATAFORMAT ),
    sizeof( DIOBJECTDATAFORMAT ),
    DIDF_ABSAXIS,
    sizeof( MouseState ),
    numMouseObjects,
    g_aObjectFormats
};


//-----------------------------------------------------------------------------
// Name: InitDirectInput()
// Desc: Initialize the DirectInput variables.
//-----------------------------------------------------------------------------
HRESULT Input::InitDirectInput( )
{
    HRESULT hr;

    // Register with the DirectInput subsystem and get a pointer
    // to a IDirectInput interface we can use.
    if( FAILED( hr = DirectInput8Create( GetModuleHandle( NULL ), DIRECTINPUT_VERSION,
                                         IID_IDirectInput8, ( VOID** )&g_pDI, NULL ) ) )
        return hr;

    // Retrieve the system mouse
    if( FAILED( g_pDI->CreateDevice( GUID_SysMouse, &g_pMouse, NULL ) ) )
    {
        MessageBox( NULL, TEXT( "Mouse not found. The sample will now exit." ),
                    TEXT( "DirectInput Sample" ),
                    MB_ICONERROR | MB_OK );
        return S_OK;
    }

    // A data format specifies which controls on a device we are interested in,
    // and how they should be reported. This tells DInput that we will be
    // passing a MouseState structure to IDirectInputDevice::GetDeviceState().
    if( FAILED( hr = g_pMouse->SetDataFormat( &g_dfMouse ) ) )
        return hr;


    return S_OK;
}




//-----------------------------------------------------------------------------
// Name: UpdateInputState()
// Desc: Get the input device's state and display it.
//-----------------------------------------------------------------------------
HRESULT Input::GetMouseState( MouseState *ms)
{
    HRESULT hr;
    TCHAR strText[128] = {0}; // Device state text

    static BOOL bInitialized = FALSE;    // Indicates offsets are valid

    if( NULL == g_pMouse )
        return S_OK;

    // Poll the device to read the current state
    hr = g_pMouse->Poll();
    if( FAILED( hr ) )
    {
        // DInput is telling us that the input stream has been
        // interrupted. We aren't tracking any state between polls, so
        // we don't have any special reset that needs to be done. We
        // just re-acquire and try again.
        hr = g_pMouse->Acquire();
        while( hr == DIERR_INPUTLOST )
            hr = g_pMouse->Acquire();

        // hr may be DIERR_OTHERAPPHASPRIO or other errors.  This
        // may occur when the app is minimized or in the process of 
        // switching, so just try again later 
        return S_OK;
    }

    // Get the input's device state
    if( FAILED( hr = g_pMouse->GetDeviceState( sizeof( MouseState ), ms ) ) )
        return hr; // The device should have been acquired during the Poll()


    return S_OK;
}




//-----------------------------------------------------------------------------
// Name: FreeDirectInput()
// Desc: Initialize the DirectInput variables.
//-----------------------------------------------------------------------------
VOID Input::FreeDirectInput()
{
    // Unacquire the device one last time just in case 
    // the app tried to exit while the device is still acquired.
    if( g_pMouse )
        g_pMouse->Unacquire();

    // Release any DirectInput objects.
    SAFE_RELEASE( g_pMouse );
    SAFE_RELEASE( g_pDI );
}